﻿// ------------------------------------------------------------------------------
// <copyright file="HSB.cs" company="skumaronline.com">
//    Copyright (C) 2012 Suresh Kumar T.  All rights reserved.
// </copyright>
// ------------------------------------------------------------------------------

namespace StoreAppLib.UI
{
    using System;

    /// <summary>
    /// Hue Saturation Brightness color
    /// </summary>
    public class HSB : BindableBase
    {
        /// <summary>
        /// Variable to hold the hue component.
        /// </summary>
        private double hue;

        /// <summary>
        /// Variable to hold the saturation component.
        /// </summary>
        private double saturation;

        /// <summary>
        /// Variable to hold the brightness component.
        /// </summary>
        private double brightness;

        /// <summary>
        /// Variable to hold the alpha component.
        /// </summary>
        private byte alpha;

        /// <summary>
        /// Initializes a new instance of the <see cref="HSB"/> class"
        /// </summary>
        public HSB()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="HSB"/> class"
        /// </summary>
        /// <param name="hue">Hue component</param>
        /// <param name="saturation">Saturation component</param>
        /// <param name="brightness">Brightness component</param>
        public HSB(double hue, double saturation, double brightness)
            : this(255, hue, saturation, brightness)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="HSB"/> class"
        /// </summary>
        /// <param name="alpha">Alpha component</param>
        /// <param name="hue">Hue component</param>
        /// <param name="saturation">Saturation component</param>
        /// <param name="brightness">Brightness component</param>
        public HSB(byte alpha, double hue, double saturation, double brightness)
        {
            this.Hue = hue;
            this.Saturation = saturation;
            this.Brightness = brightness;
            this.Alpha = alpha;
        }

        /// <summary>
        /// Gets or sets the hue component.
        /// </summary>
        /// <value>
        /// The hue component.
        /// </value>
        public double Hue
        {
            get
            {
                return this.hue;
            }

            set
            {
                this.SetProperty(ref this.hue, value > 360 ? 360 : (value < 0 ? 0 : value));
            }
        }

        /// <summary>
        /// Gets or sets the saturation component.
        /// </summary>
        /// <value>
        /// The saturation component.
        /// </value>
        public double Saturation
        {
            get
            {
                return this.saturation;
            }

            set
            {
                this.SetProperty(ref this.saturation, value > 1 ? 1 : (value < 0 ? 0 : value));
            }
        }

        /// <summary>
        /// Gets or sets the brightness component.
        /// </summary>
        /// <value>
        /// The brightness component.
        /// </value>
        public double Brightness
        {
            get
            {
                return this.brightness;
            }

            set
            {
                this.SetProperty(ref this.brightness, value > 1 ? 1 : (value < 0 ? 0 : value));
            }
        }

        /// <summary>
        /// Gets or sets the alpha component.
        /// </summary>
        /// <value>
        /// The alpha component.
        /// </value>
        public byte Alpha
        {
            get
            {
                return this.alpha;
            }

            set
            {
                this.SetProperty(ref this.alpha, value);
            }
        }

        /// <summary>
        /// Equal to operator
        /// </summary>
        /// <param name="hsb1">First color</param>
        /// <param name="hsb2">Second color</param>
        /// <returns>true if colors are equal; otherwise, false</returns>
        public static bool operator ==(HSB hsb1, HSB hsb2)
        {
            return hsb1.alpha == hsb2.alpha && hsb1.hue == hsb2.hue && hsb1.saturation == hsb2.saturation && hsb1.brightness == hsb2.brightness;
        }

        /// <summary>
        /// note equal to operator
        /// </summary>
        /// <param name="hsb1">First color</param>
        /// <param name="hsb2">Second color</param>
        /// <returns>true if colors are not equal; otherwise, false</returns>
        public static bool operator !=(HSB hsb1, HSB hsb2)
        {
            return !(hsb1 == hsb2);
        }

        /// <summary>
        /// Create HSB color from the specified values
        /// </summary>
        /// <param name="hue">The color hue</param>
        /// <param name="saturation">The color saturation</param>
        /// <param name="brightness">The color brightness</param>
        /// <returns>Returns HSB color</returns>
        public static HSB FromHSB(double hue, double saturation, double brightness)
        {
            return HSB.FromHSB(255, hue, saturation, brightness);
        }

        /// <summary>
        /// Create HSB color from the specified values
        /// </summary>
        /// <param name="alpha">The color alpha</param>
        /// <param name="hue">The color hue</param>
        /// <param name="saturation">The color saturation</param>
        /// <param name="brightness">The color brightness</param>
        /// <returns>Returns HSB color</returns>
        public static HSB FromHSB(byte alpha, double hue, double saturation, double brightness)
        {
            return new HSB(alpha, hue, saturation, brightness);
        }

        /// <summary>
        /// Determines whether the specified color is equal to this color
        /// </summary>
        /// <param name="hsb">Color to be compared with</param>
        /// <returns>true if the specified color is equal to the current color; otherwise, false</returns>
        public bool Equals(HSB hsb)
        {
            return this == hsb;
        }

        /// <summary>
        /// Determines whether the specified object is equal to this color
        /// </summary>
        /// <param name="obj">Object to be compared with</param>
        /// <returns>true if the specified object is equal to the current color; otherwise, false</returns>
        public override bool Equals(object obj)
        {
            if (obj == null || this.GetType() != obj.GetType())
            {
                return false;
            }
            else
            {
                return this == (HSB)obj;
            }
        }

        /// <summary>
        /// Serves as a hash function for a this type
        /// </summary>
        /// <returns>A hash code for the current color</returns>
        public override int GetHashCode()
        {
            return this.alpha.GetHashCode() ^ this.hue.GetHashCode() ^ this.saturation.GetHashCode() ^ this.brightness.GetHashCode();
        }

        /// <summary>
        /// Gets the ARGB color (Windows.UI.Color) of this color
        /// </summary>
        /// <returns>Returns Windows.UI.Color</returns>
        public Windows.UI.Color ToColor()
        {
            double r = 0;
            double g = 0;
            double b = 0;

            if (this.saturation == 0.0)
            {
                r = g = b = this.brightness;
            }
            else
            {
                // Find sector position. 6 sector in 360 degree.
                double sectorPosition = this.hue / 60.0;

                int sectorNumber = (int)Math.Floor(sectorPosition);

                // get the fractional part of the sector
                double fractionalSector = sectorPosition - sectorNumber;

                // calculate values for the three axes of the color. 
                double p = this.brightness * (1.0 - this.saturation);
                double q = this.brightness * (1.0 - (this.saturation * fractionalSector));
                double t = this.brightness * (1.0 - (this.saturation * (1 - fractionalSector)));

                // assign the fractional colors to r, g, and b based on the sector the angle is in.
                switch (sectorNumber)
                {
                    case 0:
                    case 6:
                        r = this.brightness;
                        g = t;
                        b = p;
                        break;
                    case 1:
                        r = q;
                        g = this.brightness;
                        b = p;
                        break;
                    case 2:
                        r = p;
                        g = this.brightness;
                        b = t;
                        break;
                    case 3:
                        r = p;
                        g = q;
                        b = this.brightness;
                        break;
                    case 4:
                        r = t;
                        g = p;
                        b = this.brightness;
                        break;
                    case 5:
                        r = this.brightness;
                        g = p;
                        b = q;
                        break;
                }
            }

            return Windows.UI.Color.FromArgb(
                            this.alpha,
                            Convert.ToByte(r * 255.0),
                            Convert.ToByte(g * 255.0),
                            Convert.ToByte(b * 255.0));
        }
    }
}
